package com.tn.rtdata.jmx;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jmx.export.MBeanExporter;

import com.tn.rtdata.DataEvent;
import com.tn.rtdata.DataEventStats;
import com.tn.rtdata.DataListener;
import com.tn.rtdata.util.ObservableMap;

/**
 * A classes that adds <code>DataEventStatsInfoMXBean</code>s when new fills are detected.
 */
public class DataEventStatsMonitor
{
  private static final Logger LOGGER = LoggerFactory.getLogger(DataEventStatsMonitor.class);

  private static final String KEY_NAME = "name";

  private Map<String, DataEventStats> dataEventStatsCache;
  private String domain;

  /**
   * Creates a new <code>DataEventStatsMonitor</code>.
   *
   * @param dataEventStatsCache the data events stats cache.
   * @param mbeanExporter the mbean exporter used to export the <code>DataEventStatsInfoMXBean</code>s.
   * @param domain the domain into which the <code>DataEventStatsInfoMXBean</code>s are exported.
   */
  public DataEventStatsMonitor(ObservableMap<String, DataEventStats> dataEventStatsCache, MBeanExporter mbeanExporter, String domain)
  {
    dataEventStatsCache.addDataListener(new DataEventStatsInfoExporter(mbeanExporter));
    this.dataEventStatsCache = dataEventStatsCache;
    this.domain = domain;
  }

  /**
   * An implementation of <code>DataListener</code> that registers a new <code>DataEventStatsInfoMXBean</code> for each unique type of
   * <code>Fill</code> that's created.
   */
  private class DataEventStatsInfoExporter implements DataListener<DataEventStats> {

    protected MBeanExporter mbeanExporter;
    protected Set<String> exportedFills;

    /**
     * Creates a new <code>DataEventStatsInfoExporter</code> initialized with the <code>mbeanExporter</code>.
     */
    public DataEventStatsInfoExporter(MBeanExporter mbeanExporter)
    {
      this.mbeanExporter = mbeanExporter;
      this.exportedFills = new HashSet<String>();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void dataEvent(DataEvent<DataEventStats> dataEvent)
    {
      String fillName = "unknown";

      if (dataEvent.getNewValue() != null)
      {
         fillName = dataEvent.getNewValue().getFillName();
      }
      else if (dataEvent.getOldValue() != null)
      {
        fillName = dataEvent.getOldValue().getFillName();
      }

      try
      {
        if (DataEvent.Type.ADD.equals(dataEvent.getType()) && !exportedFills.contains(fillName))
        {
          registerDataEventStatsInfoMBean(fillName, DataEvent.Type.INITIAL);
          registerDataEventStatsInfoMBean(fillName, DataEvent.Type.ADD);
          registerDataEventStatsInfoMBean(fillName, DataEvent.Type.UPDATE);
          registerDataEventStatsInfoMBean(fillName, DataEvent.Type.DELETE);
          exportedFills.add(fillName);

          LOGGER.info("Registered MBeans for fill: {}", fillName);
        }
      }
      catch (Exception e)
      {
        LOGGER.error("An error occurred adding MBean for fill: " + fillName, e);
      }
    }

    /**
     * Registers an <code>DataEventStatsInfoMXBean</code> for the <code>fillName</code> and <code>dataEventType</code>.
     */
    private void registerDataEventStatsInfoMBean(String fillName, DataEvent.Type dataEventType) throws MalformedObjectNameException
    {
      ObjectName objectName = new ObjectName(domain, KEY_NAME, fillName + "." + dataEventType.toString().toLowerCase());
      mbeanExporter.registerManagedResource(new DataEventStatsInfo(dataEventStatsCache, fillName, dataEventType), objectName);
      LOGGER.debug("Registered MBean: {}", objectName.getCanonicalName());
    }
  }
}
